import cv2 as cv
import numpy as np 
import matplotlib.pyplot as plt
import copy

# 1.以Lena图像为原始图像，通过OpenCV实现平均滤波，高斯滤波及中值滤波，比较滤波结果。

image0 = cv.imread("D:/AI_code/lena.jpg")
image0_blur = cv.blur(image0, (5, 5))                           # 均值滤波
image0_GaussianBlur = cv.GaussianBlur(image0, (5, 5), 0)        # 高斯滤波
image0_medianBlur = cv.medianBlur(image0, 5)                    # 中值滤波

names = ["blur", "GaussianBlur", "medianBlur"]
images = [image0_blur, image0_GaussianBlur, image0_medianBlur]
for i in range(0, 3):
    cv.imshow(names[i], images[i])
    cv.waitKey()
cv.destroyAllWindows()
'''
比较结果：
从以上三幅图片可以看出，均值滤波较为模糊，中值滤波去噪同时也滤掉了许多细节信息（比如Lena的睫毛），
高斯滤波去噪效果较好且保留了细节信息，图片较为清晰。
'''

# 2.以Lena为原始图像，通过OpenCV使用Sobel算子及Canny算子检测，比较边缘检测结果。

image1 = cv.cvtColor(image0, cv.COLOR_BGR2GRAY)
# Sobel算子
image_sobel_x = cv.Sobel(image1, -1, 1, 0)                 # X方向卷积
cv.imshow("sobel_x", image_sobel_x)
cv.waitKey()
image_sobel_y = cv.Sobel(image1, -1, 0, 1)                 # Y方向卷积
cv.imshow("sobel_y", image_sobel_y)
cv.waitKey()
# Canny算子
image_Canny = cv.Canny(image1, 100, 200)
cv.imshow("image_canny", image_Canny)
cv.waitKey()
cv.destroyAllWindows()
'''
比较结果：
sobel边缘检测算子得到的图像边缘较为杂乱，有的地方边缘较粗，且帽子上的噪声边缘也显示了出来。
Canny边缘检测有滤波的作用，得到的结果较为清晰简洁。边缘细且不易断开，lena脸部轮廓更加明显。 
'''

# 3.在OpenCV安装目录下找到课程对应演示图片，首先计算灰度直方图，进一步使用大津算法进行分割，并比较分割结果。

image_rice = cv.imread("D:/AI_code/rice.png")
image2 = cv.cvtColor(image_rice, cv.COLOR_BGR2GRAY)
hist_cv = cv.calcHist(image2, [0], None, [256], [0, 256])          # 计算灰度直方图
fig1 = plt.figure(1)
plt.xlabel("Bins")
plt.ylabel("Number of pixels")
plt.plot(hist_cv)
plt.draw()                                                         # 画出灰度直方图
plt.pause(2)
plt.close(fig1)
ret1, image_OTSU = cv.threshold(image2, 0, 0xff, cv.THRESH_OTSU)   # 大津算法
cv.imshow("image_OTSU", image_OTSU)
print(ret1)
cv.waitKey()
cv.destroyAllWindows()
'''
使用大津算法自动选取阈值对图像进行二值化处理。选定的阈值可以分割出绝大多数米粒。
'''

# 4.使用米粒图像，分割得到各米粒首先计算各区域（米粒）的面积、长度等信息，
#   进一步计算面积、长度的均值及方差，分析落在3sigma范围内米粒的数量。

image3 = cv.imread("D:/AI_code/rice.png")
image_gray = cv.cvtColor(image3, cv.COLOR_BGR2GRAY)
_, image4 = cv.threshold(image_gray, 0, 0xff, cv.THRESH_OTSU)           # 大津算法阈值分割
element = cv.getStructuringElement(cv.MORPH_CROSS, (3, 3))
image4 = cv.morphologyEx(image4, cv.MORPH_OPEN, element)                # 开运算
image4_copy = copy.deepcopy(image4)
bin, cnts, hier = cv.findContours(image4_copy, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)  # 获取边缘信息

count = 0                                      # 区域（米粒）数量
list_area = []                                 # 新建列表存储各个区域的面积
list_length = []                               # 新建列表存储各个区域的长度

for i in range(len(cnts), 0, -1):
    c = cnts[i-1]
    area = cv.contourArea(c)
    if area < 10:
        continue
    count = count + 1
    print("blob", i, ":", area)                # 打印区域面积
    list_area.append(area)                     # 生成面积列表
    x, y, w, h = cv.boundingRect(c)
    if w >= h:
        length = w
    else:
        length = h
    print("length", i, ":", length)            # 打印区域长度
    list_length.append(length)                 # 生成长度列表
    cv.rectangle(image3, (x, y), (x+w, y+h), (0, 0, 0xff), 1)

area_average = np.mean(list_area)              # 利用numpy求取面积的均值
print("面积均值：", area_average)
area_variance = np.var(list_area)              # 利用numpy求取面积的方差
print("面积方差：", area_variance)

length_average = np.mean(list_length)              # 利用numpy求取长度的均值
print("长度均值：", length_average)
length_variance = np.var(list_length)              # 利用numpy求取长度的方差
print("长度方差：", length_variance)

print("米粒数量:", count)
cv.imshow("threshold", image4)
cv.waitKey()
cv.imshow("image3", image3)
cv.waitKey()
cv.destroyAllWindows()

# 5.使用棋盘格及自选风景图像，分别使用SIFT、FAST、ORB算子检测角点，并分析检测结果、
#   使用Harris角点检测算子检测棋盘格，并与上述结果比较

# FAST算子角点检测：
image10 = cv.imread("D:/AI_code/home10.jpg")
fast = cv.FastFeatureDetector_create(threshold=40, nonmaxSuppression=True, \
                                     type=cv.FAST_FEATURE_DETECTOR_TYPE_9_16)    # 创建算子
kp1 = fast.detect(image10, None)                                                 # 检测并返回关键点
cv.drawKeypoints(image10, kp1, image10, color=(255, 255, 255))                   # 绘制关键点
cv.imshow("FAST", image10)
cv.waitKey()
# cv.destroyAllWindows()

# SIFT算子角点检测：
image11 = cv.imread("D:/AI_code/home10.jpg")
sift = cv.xfeatures2d.SIFT_create()                                         # 创建算子
kp2, descriptor = sift.detectAndCompute(image11, None)                      # 检测并返回关键点、描述子
image11 = cv.drawKeypoints(image = image11, outImage=image11, \
                           keypoints=kp2, flags=cv.DRAW_MATCHES_FLAGS_DEFAULT, color=(0, 200, 200))
cv.imshow("SIFT", image11)
cv.waitKey()

# ORB算子角点检测：
image12 = cv.imread("D:/AI_code/home10.jpg")
orb = cv.ORB_create()                                                       # 创建算子
kp2 = orb.detect(image12, None)                                             # 检测，返回关键点
kp2, des2 = orb.compute(image12, kp2)                                       # 提取特征描述向量
image12 = cv.drawKeypoints(image=image12, outImage=image12, \
                           keypoints=kp2, flags=0, color=(0, 255, 0))
cv.imshow("ORB", image12)
cv.waitKey()
cv.destroyAllWindows()

# Harris角点检测：
img = cv.imread("D:/AI_code/chessboard1.jpg")
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
cv.imshow("image5", img)                            # 显示原图
cv.waitKey()
gray = np.float32(gray)                             # 输入图像必须是32位
dst = cv.cornerHarris(gray, 2, 3, 0.04)             # Harris角点检测算子
img[dst > 0.01 * dst.max()] = [0, 0, 255]           # 标记角点
cv.imshow("Harris", img)                               # 显示标记角点后的图像
cv.waitKey()
cv.destroyAllWindows()
'''
用FAST算子进行角点检测速度很快，但存在很多误检。
用SIFT算子进行角点检测花费时间最长，角点检测相对准确且全面。
用ORB算子进行角点检测的速度也很快，但存在漏检。
'''

